結果整理後,應該保留這段正確的 modal:
<!-- Modal -->
<div class="modal fade" id="upload_image_modal" tabindex="-1" role="dialog" aria-labelledby="uploadImageModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="uploadImageModalLabel">上傳圖片</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form id="upload_form" method="POST" action="/admin/products/upload-image" enctype="multipart/form-data">
@csrf
<input type="hidden" id="product_id" name="product_id">
<div class="form-group">
<label for="product_image">選擇圖片</label>
<input type="file" class="form-control" id="product_image" name="product_image">
</div>
<button type="submit" class="btn btn-primary">上傳</button>
</form>
</div>
</div>
</div>
</div>
✅ 補充:路由對應確認
你需要在 web.php 或相關 route 檔中加入這條對應圖片上傳的 POST 路由:
Route::post('admin/products/upload-image', 'Admin\ProductController@uploadImage');
以及控制器方法:
public function uploadImage(Request $request)
{
$request->validate([
'product_id' => 'required|integer|exists:products,id',
'product_image' => 'required|image|max:2048',
]);
$product = Product::findOrFail($request->product_id);
$path = $request->file('product_image')->store('product_images', 'public');
// 假設你有個 image_path 欄位儲存圖片路徑
$product->image_path = $path;
$product->save();
return redirect()->back()->with('success', '圖片上傳成功');
}
資料庫顯示:
資料表SQL
CREATE TABLE `images` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`attachable_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '來源表',
`attachable_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '來源表ID',
`path` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '路徑',
`filename` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '檔案名稱',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
後端
畫面就是自動回到這頁
程式碼
<?php
namespace App\Http\Controllers\Admin;
use Illuminate\Http\Request;
use App\Models\Product;
use App\Http\Controllers\Controller;
use App\Notifications\ProductDelivery;
class ProductController extends Controller
{
public function index(Request $request)
{
$productCount = Product::count();
$dataPerPage = 2;
$productPages = ceil($productCount / $dataPerPage);
$currentPage = isset($request->all()['page']) ? $request->all()['page'] : 1;
$products = Product::orderBy('created_at','desc')
->offset($dataPerPage * ($currentPage - 1))
->limit($dataPerPage)
->get();
return view('admin.products.index',['products' => $products,
'productCount' => $productCount,
'productPages' => $productPages]);
}
public function uploadImage(Request $request)
{
$request->validate([
'product_id' => 'required|integer|exists:products,id',
'product_image' => 'required|image|max:2048',
]);
$product = Product::findOrFail($request->product_id);
$path = $request->file('product_image')->store('product_images', 'public');
// 假設你有個 image_path 欄位儲存圖片路徑
$product->image_path = $path;
$product->save();
return redirect()->back()->with('success', '圖片上傳成功');
}
}
程式碼
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Image extends Model
{
protected $guarded = [];
public function attachable()
{
return $this->morphTo();
}
}
程式碼
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
use HasFactory;
protected $guarded =[''];
public function cartItems()
{
return $this->hasMany(CartItem::class);
}
public function orderItems()
{
return $this->hasMany(OrderItem::class);
}
public function checkQuantity($quantity)
{
if ($this->quantity < $quantity) {
return false;
}
return true;
}
public function images()
{
return $this->morphMany(Image::class, 'attachable');
}
}
程式碼
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateImages extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('images', function (Blueprint $table) {
$table->id();
$table->string('attachable_type', 255)->comment('來源表');
$table->string('attachable_id', 255)->comment('來源表ID');
$table->string('path', 255)->comment('路徑');
$table->string('filename', 255)->comment('檔案名稱');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('images');
}
}
程式碼
@extends('layouts.admin_app')
@section('content')
<h2>產品列表</h2>
<span>產品總數: {{ $productCount }} </span>
<table>
<thead>
<tr>
<td>編號</td>
<td>標題</td>
<td>內容</td>
<td>價格</td>
<td>數量</td>
<td>圖片</td>
<td>功能</td>
</tr>
</thead>
<tbody>
@foreach( $products as $product )
<tr>
<td>{{ $product->id }}</td>
<td>{{ $product->title }}</td>
<td>{{ $product->content }}</td>
<td>{{ $product->price }}</td>
<td>{{ $product->quantity }}</td>
<td></td>
<td>
<input type="button" class="upload_image" data-id="{{ $product->id }}" value="上傳圖片">
</td>
</tr>
@endforeach
</tbody>
</table>
<div>
@for ($i = 1; $i <= $productPages; $i++)
<a href="/admin/products?page={{ $i }}">第 {{ $i }} 頁</a>
@endfor
</div>
<!-- Add Modal for Image Upload -->
<div class="modal fade" id="upload_image_modal" tabindex="-1" role="dialog" aria-labelledby="uploadImageModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="uploadImageModalLabel">上傳圖片</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<!-- <form id="upload_form" method="POST" enctype="multipart/form-data">-->
<form id="upload_form" method="POST" action="/admin/products/upload-image" enctype="multipart/form-data">
@csrf
<input type="hidden" id="product_id" name="product_id">
<div class="form-group">
<label for="product_image">選擇圖片</label>
<input type="file" class="form-control" id="product_image" name="product_image">
</div>
<button type="submit" class="btn btn-primary">上傳</button>
</form>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function() {
$('.upload_image').click(function() {
$('#product_id').val($(this).data('id'));
$('#upload_image_modal').modal('show');
});
});
</script>
@endsection
程式碼
Route::post('admin/products/upload-image', 'Admin\ProductController@uploadImage');
程式碼
<!-- Modal -->
<div class="modal fade" id="upload_image_modal" tabindex="-1" role="dialog" aria-labelledby="uploadImageModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="uploadImageModalLabel">上傳圖片</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form id="upload_form" method="POST" action="/admin/products/upload-image" enctype="multipart/form-data">
@csrf
<input type="hidden" id="product_id" name="product_id">
<div class="form-group">
<label for="product_image">選擇圖片</label>
<input type="file" class="form-control" id="product_image" name="product_image">
</div>
<button type="submit" class="btn btn-primary">上傳</button>
</form>
</div>
</div>
</div>
</div>
修改 圖片 放的位置
原來的位置
修改後的位置
修改後的程式碼
<?php
namespace App\Http\Controllers\Admin;
use Illuminate\Http\Request;
use App\Models\Product;
use App\Http\Controllers\Controller;
use App\Notifications\ProductDelivery;
class ProductController extends Controller
{
public function index(Request $request)
{
$productCount = Product::count();
$dataPerPage = 2;
$productPages = ceil($productCount / $dataPerPage);
$currentPage = isset($request->all()['page']) ? $request->all()['page'] : 1;
$products = Product::orderBy('created_at','desc')
->offset($dataPerPage * ($currentPage - 1))
->limit($dataPerPage)
->get();
return view('admin.products.index',['products' => $products,
'productCount' => $productCount,
'productPages' => $productPages]);
}
/*public function uploadImage(Request $request)
{
$request->validate([
'product_id' => 'required|integer|exists:products,id',
'product_image' => 'required|image|max:2048',
]);
$product = Product::findOrFail($request->product_id);
$path = $request->file('product_image')->store('product_images', 'public');
// 假設你有個 image_path 欄位儲存圖片路徑
$product->image_path = $path;
$product->save();
return redirect()->back()->with('success', '圖片上傳成功');
}*/
public function uploadImage(Request $request)
{
$request->validate([
'product_id' => 'required|integer|exists:products,id',
'product_image' => 'required|image|max:2048',
]);
$product = Product::findOrFail($request->product_id);
$path = $request->file('product_image')->store('images', 'public');
$filename = basename($path);
// Create a new image record in the images table instead of updating products table
$product->images()->create([
'path' => $path,
'filename' => $filename,
'attachable_type' => 'App\Models\Product',
'attachable_id' => $product->id,
]);
return redirect()->back()->with('success', '圖片上傳成功');
}
}
拿到指定的路徑
修改程式碼
上面也要加上
下指令
修改前端程式碼
下指令啟動
上傳成功的程式碼
<!-- 圖片上傳 Modal -->
<div class="modal fade" id="upload_image_modal" tabindex="-1" role="dialog" aria-labelledby="uploadImageModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="uploadImageModalLabel">上傳圖片</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form id="upload_image_form" method="POST" action="/admin/products/upload-image" enctype="multipart/form-data">
@csrf
<input type="hidden" id="product_id" name="product_id" value="">
<div class="form-group">
<label for="product_image">選擇圖片</label>
<input type="file" class="form-control" id="product_image" name="product_image" accept="image/*">
</div>
<button type="submit" class="btn btn-primary">上傳</button>
</form>
</div>
</div>
</div>
</div>
<!-- Excel 匯入 Modal -->
<div class="modal fade" id="import" tabindex="-1" role="dialog" aria-labelledby="importModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="importModalLabel">匯入EXCEL</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form id="import_form" method="POST" action="/admin/products/excel/import" enctype="multipart/form-data">
@csrf
<div class="form-group">
<label for="excel">選擇 Excel 檔案</label>
<input type="file" class="form-control" id="excel" name="excel" accept=".xlsx,.xls,.csv">
</div>
<button type="submit" class="btn btn-primary">匯入</button>
</form>
</div>
</div>
</div>
</div>
<script>
// JavaScript 用於設定產品ID
function openUploadModal(productId) {
document.getElementById('product_id').value = productId;
$('#upload_image_modal').modal('show');
}
// 表單提交前驗證
document.getElementById('upload_image_form').addEventListener('submit', function(e) {
const productId = document.getElementById('product_id').value;
const imageFile = document.getElementById('product_image').files[0];
if (!productId) {
e.preventDefault();
alert('請選擇產品');
return false;
}
if (!imageFile) {
e.preventDefault();
alert('請選擇圖片檔案');
return false;
}
// 檢查檔案大小 (2MB = 2048KB)
if (imageFile.size > 2048 * 1024) {
e.preventDefault();
alert('檔案大小不能超過 2MB');
return false;
}
// 檢查檔案類型
const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif'];
if (!allowedTypes.includes(imageFile.type)) {
e.preventDefault();
alert('請選擇有效的圖片檔案 (JPEG, PNG, GIF)');
return false;
}
});
// Excel 匯入表單驗證
document.getElementById('import_form').addEventListener('submit', function(e) {
const excelFile = document.getElementById('excel').files[0];
if (!excelFile) {
e.preventDefault();
alert('請選擇 Excel 檔案');
return false;
}
const allowedTypes = [
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', // .xlsx
'application/vnd.ms-excel', // .xls
'text/csv' // .csv
];
if (!allowedTypes.includes(excelFile.type)) {
e.preventDefault();
alert('請選擇有效的 Excel 檔案 (.xlsx, .xls, .csv)');
return false;
}
});
</script>
電商管理平台
http://127.0.0.1:8000/admin/products
-- Host: localhost Database: laravel_demo
-- Server version 8.0.41
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT /;
/!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS /;
/!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION /;
/!50503 SET NAMES utf8 /;
/!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE /;
/!40103 SET TIME_ZONE='+00:00' /;
/!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 /;
/!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 /;
/!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' /;
/!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
images
DROP TABLE IF EXISTS images
;
/*!40101 SET @saved_cs_client = @@character_set_client /;
/!50503 SET character_set_client = utf8mb4 /;
CREATE TABLE images
(id
bigint unsigned NOT NULL AUTO_INCREMENT,attachable_type
varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '來源表',attachable_id
varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '來源表ID',path
varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '路徑',filename
varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '檔案名稱',created_at
timestamp NULL DEFAULT NULL,updated_at
timestamp NULL DEFAULT NULL,
PRIMARY KEY (id
)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/!40101 SET character_set_client = @saved_cs_client */;
images
LOCK TABLES images
WRITE;
/*!40000 ALTER TABLE images
DISABLE KEYS /;
INSERT INTO images
VALUES (1,'App\Models\Product','2','images/gGzw2Y0y9fyype8tu0KXKrbjsDN9llWLz1hu6px8.png','1x1_和服.png','2025-05-08 15:30:10','2025-05-08 15:30:10'),(8,'App\Models\Product','1','images/2CeP1jTIXxIAywLQkfWektiezeDuQhUlunqb6VZX.jpg','2CeP1jTIXxIAywLQkfWektiezeDuQhUlunqb6VZX.jpg','2025-05-10 04:10:16','2025-05-10 04:10:16');
/!40000 ALTER TABLE images
ENABLE KEYS /;
UNLOCK TABLES;
/!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE /;
/!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS /;
/!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS /;
/!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT /;
/!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS /;
/!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION /;
/!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2025-08-29 22:45:10
大家明天見~